﻿using System;
using System.Collections.Generic;
using Facebook;


namespace Tequila.Phone.Sharing.Facebook
{
    /// <summary>
    /// Facebook sharing service.
    /// </summary>
    public class FacebookShareService : BaseShareService
    {
        private const string ExtendedPermissions = "publish_stream";
        private const string SettingsTokenKey = "facebook_access_token";

        private readonly FacebookOAuthClient _oauthClient;
        private readonly IFileStorageManager _fileStorageManager;
        private readonly ISettingsService _settingsService;

        private Uri _loginUrl;

        /// <summary>
        /// Initializes new instance of class.
        /// </summary>
        /// <param name="settingsService"></param>
        /// <param name="fileStorageManager"></param>
        /// <param name="session"></param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ShareServiceException"></exception>
        public FacebookShareService(ISettingsService settingsService,
            IFileStorageManager fileStorageManager, FacebookAuthorizationSession session)
        {
            if (settingsService == null)
            {
                throw new ArgumentNullException("settingsService");
            }
            if (fileStorageManager == null)
            {
                throw new ArgumentNullException("fileStorageManager");
            }
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (!session.IsValid)
            {
                throw new ShareServiceException(ShareServiceErrorType.InvalidSession);
            }

            _settingsService = settingsService;
            _fileStorageManager = fileStorageManager;
            _oauthClient = new FacebookOAuthClient { AppId = session.AppId, AppSecret = session.AppSecret };

            BuildLoginUri();
        }

        /// <summary>
        /// Determines whether authentication performed or not.
        /// </summary>
        /// <returns></returns>
        public override bool IsAuthenticated()
        {
            return _settingsService.Contains(SettingsTokenKey);
        }

        /// <summary>
        /// Returns OAUTH login <see cref="T:System.Uri">uri</see>.
        /// </summary>
        /// <returns>
        /// Type: <see cref="T:System.Uri"/>.
        /// </returns>
        public override Uri GetOAuthLoginUri()
        {
            return _loginUrl;
        }

        private void OnExchangeCompleted(object sender, FacebookApiEventArgs args)
        {
            _oauthClient.ExchangeCodeForAccessTokenCompleted -= OnExchangeCompleted;

            // make sure to check that no error has occurred.
            if (args.Error != null)
            {
                OnLoginError(args.Error.Message);
            }
            else
            {
                var result = (IDictionary<string, object>)args.GetResultData();
                var accessToken = (string)result["access_token"];

                _settingsService.Save(SettingsTokenKey, accessToken);
                OnLoginSuccess();
            }
        }

        /// <summary>
        /// Processes OAUTH output requested by <see cref="T:System.Uri">uri</see>.
        /// </summary>
        public override void ProcessOAuthBrowserResult(Uri uri)
        {
            // whenever the browser navigates to a new url, try parsing the url
            // the url may be the result of OAuth 2.0 authentication.
            FacebookOAuthResult oauthResult;

            if (FacebookOAuthResult.TryParse(uri, out oauthResult))
            {
                // The url is the result of OAuth 2.0 authentication.
                if (oauthResult.IsSuccess)
                {
                    // we got the code here
                    var code = oauthResult.Code;

                    _oauthClient.ExchangeCodeForAccessTokenCompleted += OnExchangeCompleted;
                    _oauthClient.ExchangeCodeForAccessTokenAsync(code);
                }
                else
                {
                    OnLoginError(oauthResult.ErrorDescription);
                    // the user clicked don't allow or some other error occurred.
                    //MessageBox.Show(oauthResult.ErrorDescription);
                }
            }
        }

        /// <summary>
        /// Returns logout url.
        /// </summary>
        /// <returns>
        /// Type: <see cref="T:System.Uri"/>.
        /// </returns>
        public override Uri GetLogoutUrl()
        {
            Uri uri = null;

            if (_settingsService.Contains(SettingsTokenKey))
            {
                var url = string.Format("https://www.facebook.com/logout.php?access_token={0}&confirm=1&next={1},",
                                        _settingsService.Get(SettingsTokenKey) as string, _loginUrl);
                uri = new Uri(url);
            }

            return uri;
        }

        /// <summary>
        /// Creates custom Login uri.
        /// </summary>
        public override sealed void BuildLoginUri()
        {
            // Make sure to set the app id.

            var loginParameters = new Dictionary<string, object>();

            // The requested response: an access token (token), an authorization code (code), or both (code token).
            // note: there is a bug in wpf browser control which ignores the fragment part (#) of the url
            // so we cannot get the access token. To fix this, set response_type to code as code is set in
            // the querystring.
            loginParameters["response_type"] = "code";
            loginParameters["display"] = "wap";

            // add the 'scope' parameter only if we have extendedPermissions.
            if (!string.IsNullOrEmpty(ExtendedPermissions))
            {
                // A comma-delimited list of permissions
                loginParameters["scope"] = ExtendedPermissions;
            }


            // when the Form is loaded navigate to the login url.
            _loginUrl = _oauthClient.GetLoginUrl(loginParameters);

        }

        /// <summary>
        /// Shares <see cref="GaleryImage">image</see>.
        /// </summary>
        /// <param name="image"></param>
        public override void PostImage(GaleryImage image)
        {
            Post(BuildImageParameters(image));
        }

        /// <summary>
        /// Shares <see cref="Article">article</see>.
        /// </summary>
        /// <param name="article"></param>
        public override void Post(Article article)
        {
            Post(BuildArticleParameters(article, false));
        }

        private Dictionary<string, object> BuildArticleParameters(Article article, bool isFull)
        {
            if(article == null)
            {
                OnPostError("Invalid message to post.");
                return null;
            }
            var htmlContent = string.Empty;
            var plainContent = string.Empty;

            if (!string.IsNullOrEmpty(article.Link))
            {
                _fileStorageManager.ReadFile(article.Link);
                plainContent = htmlContent.TransformHtmlToPlain();
            }

            //const int maxContentLength = 200;
            //if (plainContent.Length > maxContentLength)
            //    plainContent = string.Format("{0}...", plainContent.Substring(0, maxContentLength));

            var parameters = new Dictionary<string, object>();
            parameters["name"] = string.Format("{0}", article.Title);
            parameters["link"] = article.OriginalLink;
            if (!isFull)
                parameters["message"] = article.Message;
            else
                parameters["message"] = plainContent;
            if (!string.IsNullOrEmpty(article.ThumbnailOriginalLink))
                parameters["picture"] = article.ThumbnailOriginalLink;

            parameters["description"] = article.Description;

            return parameters;
        }

        private static Dictionary<string, object> BuildImageParameters(GaleryImage image)
        {
            var parameters = new Dictionary<string, object>();
            var title = string.Format("{0}. {1}", image.GalleryTitle, image.Title);
            if (!string.IsNullOrEmpty(image.By))
                title += string.Format(" ({0})", image.By);
            parameters["name"] = title;
            parameters["link"] = image.OriginalLink.Replace("iphone", "www");
            parameters["picture"] = image.OriginalLink;

            return parameters;
        }


        private void Post(Dictionary<string, object> parameters)
        {
            var accessToken = _settingsService.Get(SettingsTokenKey) as string;

            var fb = new FacebookClient(accessToken);
            // make sure to add event handler for PostCompleted.
            fb.PostCompleted += (o, args) =>
            {
                // incase you support cancellation, make sure to check
                // e.Cancelled property first even before checking (e.Error!=null).
                if (args.Cancelled)
                {
                    // for this example, we can ignore as we don't allow this
                    // example to be cancelled.

                    // you can check e.Error for reasons behind the cancellation.
                    OnPostError(args.Error.Message);
                }
                else if (args.Error != null)
                {
                    OnPostError(args.Error.Message);
                }
                else
                {
                    // the request was completed successfully

                    // now we can either cast it to IDictionary<string, object> or IList<object>
                    //var result = (IDictionary<string, object>)args.GetResultData();
                    //_lastMessageId = (string)result["id"];

                    OnPostSuccess();

                }
            };

            fb.PostAsync("me/feed", parameters);
        }
    }
}
